from can.interfaces.pcan import PcanBus
import isotp
from udsoncan.connections import PythonIsoTpConnection
from udsoncan.client import Client
from udsoncan import services
import sys
import logging
import udsoncan
import os
LOG_NAME = 'toolUds'

# Refer to isotp documentation for full details about parameters
isotpParams = {
   'stmin' : 32,                          # Will request the sender to wait 32ms between consecutive frame. 0-127ms or 100-900ns with values from 0xF1-0xF9
   'blocksize' : 8,                       # Request the sender to send 8 consecutives frames before sending a new flow control message
   'wftmax' : 0,                          # Number of wait frame allowed before triggering an error
   'tx_data_length' : 8,                  # Link layer (CAN layer) works with 8 byte payload (CAN 2.0)
   'tx_data_min_length' : None,           # Minimum length of CAN messages. When different from None, messages are padded to meet this length. Works with CAN 2.0 and CAN FD.
   'tx_padding' : 0x55,                      # Will pad all transmitted CAN messages with byte 0x00.
   'rx_flowcontrol_timeout' : 1000,       # Triggers a timeout if a flow control is awaited for more than 1000 milliseconds
   'rx_consecutive_frame_timeout' : 1000, # Triggers a timeout if a consecutive frame is awaited for more than 1000 milliseconds
   'squash_stmin_requirement' : False,    # When sending, respect the stmin requirement of the receiver. If set to True, go as fast as possible.
   'max_frame_size' : 4095                # Limit the size of receive frame.
}

config = dict(udsoncan.configs.default_client_config)

config['data_identifiers'] = {
    0xF195: udsoncan.AsciiCodec(10)
}

class ToolUds():

    def __init__(self,parent) -> None:
        super(ToolUds,self).__init__()
        udsoncan.setup_logging('logging.conf')
        self.parent = parent
        self.logger = logging.getLogger(LOG_NAME)
        self.canCom = None
        self.canFuncCom = None
        self.canBus = None
        self.tpAddr = None
        self.tpStack = None

    def initSetting(self,channel,bitrate,txid,rxid,txFuncid):
        status = 0
        try:
            self.canBus = PcanBus(channel=channel,bitrate=bitrate)
            self.tpAddr = isotp.Address(isotp.AddressingMode.Normal_11bits, txid=txid, rxid=rxid)
            self.tpStack = isotp.CanStack(bus=self.canBus, address = self.tpAddr, params=isotpParams)
            self.canCom = PythonIsoTpConnection(self.tpStack)

            self.tpFuncAddr = isotp.Address(isotp.AddressingMode.Normal_11bits, txid=txFuncid, rxid=rxid)
            self.tpFuncStack = isotp.CanStack(bus=self.canBus, address = self.tpAddr, params=isotpParams)
            self.canFuncCom = PythonIsoTpConnection(self.tpFuncStack)
            status = 3
        except:
            self.logger.info(sys.exc_info()[0]) 
            status = 2
        else:
            self.logger.debug('isotp init success!')
            status = 1
        return status 
    
    def sendRequestPdu(self,reqPdu):
        if not self.canCom.is_open():
            self.canCom.open()
        try:
            self.canCom.send(reqPdu)
            respPdu = self.canCom.wait_frame(timeout=1)
        except:
            self.logger.info(sys.exc_info()[0]) 
        else:
            pass

    def udsDiagnosticSessionControl(self,session = 0,reqType=0):
        if reqType == 0:
            with Client(self.canCom, request_timeout=1, config=config) as client:
                try:
                    response = client.change_session(session)
                except:
                    exc = sys.exc_info()[1]
                    self.logger.info(exc)
                else:
                    # self.logger.info(response) 
                    pass
        else:
            pass
        
    def udsECUReset(self,session = 0,subFunction=0,reqType=0):
        if reqType == 0:
            with Client(self.canCom, request_timeout=1, config=config) as client:
                try:
                    response = client.ecu_reset(subFunction)
                except:
                    exc = sys.exc_info()[1]
                    self.logger.info(exc)
                else:
                    pass
        else:
            pass
        
    def udsSecurityAccess(self,session = 0,subFunction=0):
        pass
    
    def udsReadDataByIdentifier(self,session = 0,subFunction=0):
        with Client(self.canCom, request_timeout=10, config=config) as client:
            try:
                response = client.read_data_by_identifier_first(0xF195)
            except:
                exc = sys.exc_info()[1]
                self.logger.info(exc)
            else:
                self.logger.info(str(response)) 
    
    def udsControlDTCSetting(self):
        pass
    
    def udsCommunicationControl(self):
        pass
    
    def udsTesterPresent(self):
        pass